import service
import graphicWrappers
import blue
import collections
import geo2
import loadRequest
import trinity
import world

class GraphicClientComponent():
    __guid__ = 'component.GraphicClientComponent'

    def __init__(self):
        self.graphicID = None




class GraphicClient(service.Service):
    __guid__ = 'svc.graphicClient'
    __componentTypes__ = ['graphic']
    __dependencies__ = ['prioritizedLoadManager', 'worldSpaceClient']
    __notifyevents__ = ['OnSessionChanged']

    def __init__(self):
        service.Service.__init__(self)
        self.isServiceReady = False
        self.scenes = {}
        self.graphicRequests = {}
        self.loadedGraphics = {}



    def Run(self, *etc):
        service.Service.Run(self, *etc)
        self.isServiceReady = True



    def CreateComponent(self, name, state):
        component = GraphicClientComponent()
        if ('graphicID' in state):
            component.graphicID = state['graphicID']
        else:
            cfgGraphic = cfg.invtypes.Get(state['_typeID']).Graphic()
            if cfgGraphic:
                component.graphicID = cfgGraphic.id
        for (key, value,) in state.iteritems():
            if (key in ('cellID', 'systemID', 'objectID', 'enlightenOverrides')):
                setattr(component, key, value)

        component.renderObject = None
        return component



    def ReportState(self, component):
        report = collections.OrderedDict()
        report['graphicID'] = component.graphicID
        report['Has RenderObject'] = (component.renderObject is not None)
        if component.renderObject:
            report['Position'] = component.renderObject.GetPosition()
            report['Rotation'] = component.renderObject.GetRotationYawPitchRoll()
            report['Scale'] = component.renderObject.GetScale()
        return report



    def PrepareComponent(self, sceneID, entityID, component):
        if (component.graphicID is None):
            return 
        component.modelFile = self.GetModelFilePath(component.graphicID)
        component.graphicType = self.GetGraphicType(component.graphicID)
        component.entityID = entityID
        component.sceneID = sceneID
        if (component.graphicID not in self.loadedGraphics):
            graphicRequest = self.graphicRequests.get(component.graphicID, None)
            if (graphicRequest is None):
                graphicRequest = loadRequest.GraphicClientRequest(self, component.graphicID)
                graphicRequest.AddComponent(component)
                self.prioritizedLoadManager.Add(False, graphicRequest)
            else:
                graphicRequest.AddComponent(component)
        else:
            self._LoadComponent(component, setPosition=False)



    def SetupComponent(self, entity, component):
        if (component.graphicID is None):
            return 
        positionComponent = entity.GetComponent('position')
        if (positionComponent and (component.renderObject is not None)):
            component.renderObject.SetPosition(positionComponent.position)
            component.renderObject.SetRotationYawPitchRoll(geo2.QuaternionRotationGetYawPitchRoll(positionComponent.rotation))



    def UnRegisterComponent(self, entity, component):
        if (component.graphicID is None):
            return 
        if component.renderObject:
            component.renderObject.RemoveFromScene(self.scenes[component.sceneID])
            self.loadedGraphics[component.graphicID] -= 1
            if (self.loadedGraphics[component.graphicID] <= 0):
                del self.loadedGraphics[component.graphicID]
        else:
            component.destroyed = True



    def _LoadComponent(self, component, setPosition = True):
        if hasattr(component, 'destroyed'):
            return 
        if (component.graphicID not in self.loadedGraphics):
            self.loadedGraphics[component.graphicID] = 1
        else:
            self.loadedGraphics[component.graphicID] += 1
        renderObject = graphicWrappers.LoadAndWrap(component.modelFile, inInterior=True)
        component.renderObject = renderObject
        if (component.renderObject is None):
            renderObject = graphicWrappers.LoadAndWrap(const.BAD_ASSET_PATH_AND_FILE, inInterior=True)
            component.renderObject = renderObject
            self.LogWarn('!!! WARNING !!! Asset (', component.modelFile, ') is missing !!! WARNING !!!')
        component.renderObject.name = str(component.entityID)
        if hasattr(renderObject, 'SetCell'):
            renderObject.SetCell(getattr(component, 'cellID', '0'))
        if hasattr(renderObject, 'SetSystem'):
            renderObject.SetSystem(getattr(component, 'systemID', '0'))
        if hasattr(component, 'objectID'):
            renderObject.objectID = component.objectID
        if hasattr(renderObject, 'enlightenAreas'):
            enlightenOverrides = getattr(component, 'enlightenOverrides', [])
            for enlightenOverride in enlightenOverrides:
                for area in renderObject.enlightenAreas:
                    if (enlightenOverride.areaName == area.name):
                        enlightenArea = world.EnlightenArea(enlightenOverride)
                        area.isEmissive = enlightenArea.GetIsEmissive()
                        area.albedoColor = enlightenArea.GetAlbedoColor()
                        area.emissiveColor = enlightenArea.GetEmissiveColor()


        if setPosition:
            entity = self.entityService.FindEntityByID(component.entityID)
            if entity:
                positionComponent = entity.GetComponent('position')
                if positionComponent:
                    renderObject.SetPosition(positionComponent.position)
                    renderObject.SetRotationYawPitchRoll(geo2.QuaternionRotationGetYawPitchRoll(positionComponent.rotation))
        scene = self.GetScene(component.sceneID)
        if scene:
            component.renderObject.AddToScene(self.scenes[component.sceneID])



    def OnEntitySceneLoaded(self, sceneID):
        worldSpaceTypeID = sm.GetService('worldSpaceClient').GetWorldSpaceTypeIDFromWorldSpaceID(sceneID)
        worldSpaceRow = cfg.worldspaces.Get(worldSpaceTypeID)
        if worldSpaceRow.isInterior:
            sceneType = const.world.INTERIOR_SCENE
        else:
            sceneType = const.world.EXTERIOR_SCENE
        self.CreateScene(sceneID, sceneType)
        worldSpace = sm.GetService('worldSpaceClient').GetWorldSpace(worldSpaceTypeID).ReloadProperties(overrideScene=self.scenes[sceneID])
        if ((sceneID == session.worldspaceid) or (session.worldspaceid == 0)):
            self._AppSetRenderingScene(self.scenes[sceneID])



    def _AppSetRenderingScene(self, scene):
        raise NotImplementedError('Game specific versions of graphicClient must implement _AppSetRenderingScene')



    def OnEntitySceneUnloaded(self, sceneID):
        self.DestroyScene(sceneID)



    def CreateScene(self, sceneID, sceneType):
        if (sceneID in self.scenes):
            raise RuntimeError(('Trinity Scene Already Exists %d' % sceneID))
        if (sceneType == const.world.INTERIOR_SCENE):
            self.scenes[sceneID] = trinity.Tr2InteriorScene()
        elif (sceneType == const.world.EXTERIOR_SCENE):
            self.scenes[sceneID] = trinity.WodScene()
        elif (sceneType == const.world.EXTERIOR_FM_SCENE):
            self.scenes[sceneID] = trinity.WodExteriorScene()
        else:
            raise RuntimeError('Trying to create a nonexistent type of scene')
        graphicWrappers.Wrap(self.scenes[sceneID], convertSceneType=False)
        if hasattr(self.scenes[sceneID], 'SetID'):
            worldSpaceTypeID = sm.GetService('worldSpaceClient').GetWorldSpaceTypeIDFromWorldSpaceID(sceneID)
            self.scenes[sceneID].SetID(worldSpaceTypeID)



    def DestroyScene(self, sceneID):
        del self.scenes[sceneID]



    def GetScene(self, sceneID):
        if (sceneID in self.scenes):
            return self.scenes[sceneID]



    def GetModelFilePath(self, graphicID):
        if (prefs.ini.HasKey('noModels') and (prefs.noModels.lower() == 'true')):
            return const.BAD_ASSET_PATH_AND_FILE
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).graphicFile



    def GetCollisionFilePath(self, graphicID):
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).collisionFile



    def IsCollidable(self, graphicID):
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).collidable



    def GetPaperdollPresetPath(self, graphicID):
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).paperdollFile



    def GetTemplateID(self, graphicID):
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).animationTemplate



    def GetIsServiceReady(self):
        return self.isServiceReady



    def GetGraphicType(self, graphicID):
        if (graphicID in cfg.graphics):
            return cfg.graphics.Get(graphicID).graphicType



    def GetBoundingBox(self, graphicID):
        graphicRow = (cfg.graphics.Get(graphicID) if (graphicID in cfg.graphics) else None)
        if (graphicRow and (graphicRow.graphicMinX is not None)):
            return ((graphicRow.graphicMinX,
              graphicRow.graphicMinY,
              graphicRow.graphicMinZ), (graphicRow.graphicMaxX,
              graphicRow.graphicMaxY,
              graphicRow.graphicMaxZ))



    def OnSessionChanged(self, isRemote, sess, change):
        if (('worldspaceid' in change) and (sess.worldspaceid in self.scenes)):
            self._AppSetRenderingScene(self.scenes[sess.worldspaceid])




